package jam4j.util;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Collection;

/**
 * An {@link IOException} wrapping multiple others.
 * 
 * @author Luke Maurer
 */
public final class MultipleIOExceptions extends IOException {
    /**
     * Throw a number of I/O exceptions, wrapped if necessary in a new
     * {@link MultipleIOExceptions} object. If no arguments are given, this
     * method does nothing and returns normally.
     * 
     * @param exceptions The exceptions to throw.
     * @throws IOException If at least one exception is given.
     */
    public static void throwAll(IOException ... exceptions) 
            throws IOException {
        if (exceptions.length == 0)
            return;
        
        throw make(exceptions);
    }
    
    /**
     * Throw a number of I/O exceptions, wrapped if necessary in a new
     * {@link MultipleIOExceptions} object. If the given collection is empty,
     * this method does nothing and returns normally.
     * 
     * @param exceptions The exceptions to throw.
     * @throws IOException If at least one exception is given.
     */
    public static void throwAll(Collection<IOException> exceptions)
            throws IOException {
        throwAll(exceptions.toArray(new IOException[exceptions.size()]));
    }
    
    /**
     * Construct an IOException comprised of the given exceptions.
     * 
     * @param exceptions The exceptions to wrap. There must be at least one.
     * @return If only one exception is given, that one; otherwise a new 
     * {@link MultipleIOExceptions} object.
     * @throws IllegalArgumentException If no arguments are given.
     */
    public static IOException make(IOException ... exceptions) {
        switch (exceptions.length) {
            case 0:
                throw new IllegalArgumentException("No exceptions given");
            case 1:
                return exceptions[0];
            default:
                return new MultipleIOExceptions(exceptions);
        }
    }
    
    /**
     * Construct an IOException comprised of the given exceptions.
     * 
     * @param exceptions The exceptions to wrap. There must be at least one.
     * @return If only one exception is given, that one; otherwise a new 
     * {@link MultipleIOExceptions} object.
     * @throws IllegalArgumentException If the given collection is empty.
     */
    public static IOException make(Collection<IOException> exceptions) {
        return make(
                exceptions.toArray(new IOException[exceptions.size()]));
    }
    
    private final IOException[] exceptions;
    
    private MultipleIOExceptions(IOException ... exceptions) {
        this.exceptions = exceptions;
    }
    
    @Override
    public String getMessage() {
        final StringBuilder builder =
            new StringBuilder(getClass().getName())
                .append(": ")
                .append(exceptions.length)
                .append(" IO exceptions: ");
        
        for (int ix = 0;;) {
            builder.append(ix + 1).append(". ")
                    .append(exceptions[ix].getMessage());
            
            if (++ix == exceptions.length)
                break;
            
            builder.append("; ");
        }
        
        return builder.toString();
    }
    
    @Override
    public void printStackTrace(PrintStream s) {
        printStackTrace(new PrintWriter(s));
    }
    
    @Override
    public void printStackTrace(PrintWriter s) {
        synchronized (s) {
            s.println(this);
            for (int ix = 0; ix < exceptions.length; ix++) {
                s.print(ix + 1);
                s.print(". ");
                exceptions[ix].printStackTrace(s);
            }
        }
    }
}